package net.csdn.business.common.oauth2.component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.oauth2.model.LoginUser;
import net.csdn.business.common.domain.request.oauth.NewOauthAccessTokenDTO;
import net.csdn.business.common.domain.request.oauth.NewOauthRefreshTokenDTO;
import net.csdn.business.common.domain.vo.oauth.OauthAccessTokenVO;
import net.csdn.business.common.domain.vo.oauth.OauthRefreshTokenVO;
import net.csdn.business.common.oauth2.service.IOauthAccessTokenService;
import net.csdn.business.common.oauth2.service.IOauthRefreshTokenService;
import org.springframework.context.annotation.Primary;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@Slf4j
@Primary
@RequiredArgsConstructor
public class CustomJdbcTokenStore implements TokenStore {

    private final IOauthAccessTokenService oauthAccessTokenService;

    private final IOauthRefreshTokenService oauthRefreshTokenService;

    private AuthenticationKeyGenerator authenticationKeyGenerator = new CustomAuthenticationKeyGenerator();

    @Override
    public OAuth2Authentication readAuthentication(OAuth2AccessToken oAuth2AccessToken) {
        return this.readAuthentication(oAuth2AccessToken.getValue());
    }

    @Override
    public OAuth2Authentication readAuthentication(String token) {
        OAuth2Authentication authentication = null;
        try {
            //查询数据库，然后反序列化
            String tokenParam=this.extractTokenKey(token);
            log.info("tokenStore_readAuthentication：{}-querydb",token);
            OauthAccessTokenVO  oauthAccessTokenVO=oauthAccessTokenService.selectByTokenId(tokenParam);
            if(oauthAccessTokenVO!=null){
                Date expireTime=oauthAccessTokenVO.getExpirTime();
                //判断是否过期,如果过期，则删除过期token，并返回null
                if(isExpired(expireTime)){
                    this.removeAccessToken(token);
                    return authentication;
                }
                authentication = this.deserializeAuthentication(oauthAccessTokenVO.getAuthentication());
            }
        } catch (IllegalArgumentException var5) {
            log.warn("Failed to deserialize authentication for {} {}" , token, var5);
            this.removeAccessToken(token);
        }
        return authentication;
    }

    @Override
    public void storeAccessToken(OAuth2AccessToken oauth2AccessToken, OAuth2Authentication authentication) {
        String accessToken=oauth2AccessToken.getValue();
        String refreshToken = null;
        if (oauth2AccessToken.getRefreshToken() != null) {
            refreshToken = oauth2AccessToken.getRefreshToken().getValue();
        }
        if (this.readAccessToken(accessToken) != null) {
            this.removeAccessToken(accessToken);
        }
        Integer tokenType=1;
        //如果token以ag开头，则为私人令牌
        if(accessToken.startsWith("atp")){
            tokenType=2;
        }
        //保存到数据库
        NewOauthAccessTokenDTO oauthAccessToken=new NewOauthAccessTokenDTO();
        String tokenParam=this.extractTokenKey(accessToken);
        oauthAccessToken.setTokenId(tokenParam);
        byte[] tokenSeri=this.serializeAccessToken(oauth2AccessToken);
        oauthAccessToken.setToken(tokenSeri);
        String authenticationId=this.authenticationKeyGenerator.extractKey(authentication);
        oauthAccessToken.setAuthenticationId(authenticationId);
        Boolean isClient=authentication.isClientOnly();
        if(!isClient){
            LoginUser loginUser=(LoginUser)authentication.getPrincipal();
            String userName=authentication.getName();
            oauthAccessToken.setUserName(userName);
            oauthAccessToken.setCreatedBy(loginUser.getId());
        }
        String clientId=authentication.getOAuth2Request().getClientId();
        oauthAccessToken.setClientId(clientId);
        byte[] authenticationSeri=this.serializeAuthentication(authentication);
        oauthAccessToken.setAuthentication(authenticationSeri);
        String refreshTokenParam=this.extractTokenKey(refreshToken);
        oauthAccessToken.setRefreshToken(refreshTokenParam);
        oauthAccessToken.setExpirTime(oauth2AccessToken.getExpiration());
        oauthAccessToken.setTokenType(tokenType);
        oauthAccessTokenService.insertOauthAccessToken(oauthAccessToken);
    }

    @Override
    public OAuth2AccessToken readAccessToken(String token) {
        OAuth2AccessToken accessToken = null;
        try {
            //根据token查询数据库
            String tokenParam=this.extractTokenKey(token);
            log.info("tokenStore_readAccessToken：{}-querydb",token);
            OauthAccessTokenVO oauthAccessTokenVO=oauthAccessTokenService.selectByTokenId(tokenParam);
            if(oauthAccessTokenVO!=null){
                accessToken = (OAuth2AccessToken)this.deserializeAccessToken(oauthAccessTokenVO.getToken());
            }
        }  catch (IllegalArgumentException var5) {
            log.warn("Failed to deserialize access token for {},{}" , token, var5);
            this.removeAccessToken(token);
        }
        return accessToken;
    }

    @Override
    public void removeAccessToken(OAuth2AccessToken oAuth2AccessToken) {
        this.removeAccessToken(oAuth2AccessToken.getValue());
    }

    public void removeAccessToken(String tokenValue) {
      //根据tokenId删除
       String tokenId= this.extractTokenKey(tokenValue);
       oauthAccessTokenService.deleteByTokenId(tokenId);
    }


    @Override
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
        OAuth2AccessToken accessToken = null;
        String key = this.authenticationKeyGenerator.extractKey(authentication);
        try {
            //根据authentication_id查询数据库
            log.info("tokenStore_getAccessToken--querydb");
            OauthAccessTokenVO oauthAccessTokenVO=oauthAccessTokenService.selectByAuthentId(key);
            if(oauthAccessTokenVO!=null){
                accessToken =this.deserializeAccessToken(oauthAccessTokenVO.getToken());
            }
        }  catch (IllegalArgumentException var6) {
            log.error("Could not extract access token for authentication {},{}" ,authentication, var6);
        }
        if (accessToken != null && !key.equals(this.authenticationKeyGenerator.extractKey(this.readAuthentication(accessToken.getValue())))) {
            this.removeAccessToken(accessToken.getValue());
            this.storeAccessToken(accessToken, authentication);
        }
        return accessToken;
    }

    @Override
    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
        //数据库保存refreshToken
        NewOauthRefreshTokenDTO oauthRefreshToken=new NewOauthRefreshTokenDTO();
        String tokenParam=this.extractTokenKey(refreshToken.getValue());
        oauthRefreshToken.setTokenId(tokenParam);
        //判断是否为客户端模式
        Boolean isClient=authentication.isClientOnly();
        if(!isClient){
            LoginUser loginUser=(LoginUser)authentication.getPrincipal();
            String userName=authentication.getName();
            oauthRefreshToken.setUserName(userName);
            oauthRefreshToken.setCreatedBy(loginUser.getId());
        }
        String clientId=authentication.getOAuth2Request().getClientId();
        oauthRefreshToken.setClientId(clientId);
        byte[] tokenSeri=this.serializeRefreshToken(refreshToken);
        oauthRefreshToken.setToken(tokenSeri);
        byte[] authenticationSeri=this.serializeAuthentication(authentication);
        oauthRefreshToken.setAuthentication(authenticationSeri);
        oauthRefreshTokenService.insertOauthRefreshToken(oauthRefreshToken);
    }

    @Override
    public OAuth2RefreshToken readRefreshToken(String token) {
        OAuth2RefreshToken refreshToken = null;
        try {
            //数据库查询refreshToken
            String tokenParam=this.extractTokenKey(token);
            log.info("tokenStore_readRefreshToken：{}-querydb",token);
            OauthRefreshTokenVO oauthRefreshTokenVO= oauthRefreshTokenService.selectByTokenId(tokenParam);
            if(oauthRefreshTokenVO!=null){
                refreshToken = (OAuth2RefreshToken)this.deserializeRefreshToken(oauthRefreshTokenVO.getToken());
            }
        } catch (IllegalArgumentException var5) {
            log.warn("Failed to deserialize refresh token for token {},{}" , token, var5);
            this.removeRefreshToken(token);
        }
        return refreshToken;
    }

    @Override
    public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
        return this.readAuthenticationForRefreshToken(oAuth2RefreshToken.getValue());
    }

    public OAuth2Authentication readAuthenticationForRefreshToken(String tokenValue) {
        OAuth2Authentication authentication = null;
        try {
            //查询数据库
            String tokenParam=this.extractTokenKey(tokenValue);
            log.info("tokenStore_readAuthenticationForRefreshToken：{}-querydb",tokenValue);
            OauthRefreshTokenVO oauthRefreshTokenVO= oauthRefreshTokenService.selectByTokenId(tokenParam);
            if(oauthRefreshTokenVO!=null){
                authentication = (OAuth2Authentication)this.deserializeAuthentication(oauthRefreshTokenVO.getAuthentication());
            }
        } catch (IllegalArgumentException var5) {
            log.warn("Failed to deserialize access token for {},{}" , tokenValue, var5);
            this.removeRefreshToken(tokenValue);
        }
        return authentication;
    }

    @Override
    public void removeRefreshToken(OAuth2RefreshToken oAuth2RefreshToken) {
        this.removeRefreshToken(oAuth2RefreshToken.getValue());
    }

    public void removeRefreshToken(String token) {
        // 数据库删除
        String tokenParam=this.extractTokenKey(token);
        oauthRefreshTokenService.deleteByTokenId(tokenParam);
    }

    @Override
    public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
        this.removeAccessTokenUsingRefreshToken(refreshToken.getValue());
    }


    public void removeAccessTokenUsingRefreshToken(String refreshToken) {
        //数据库删除
       String tokenParam= this.extractTokenKey(refreshToken);
       oauthAccessTokenService.deleteByRefreshTokenId(tokenParam);
    }



    @Override
    public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
        Collection<OAuth2AccessToken> accessTokens = null;
        try {
            accessTokens=new ArrayList<>();
            log.info("tokenStore_findTokensByClientIdAndUserName：clientId:{},userName:{}-querydb",clientId,userName);
            List<OauthAccessTokenVO> oauthAccessTokens=oauthAccessTokenService.selectByClientIdAndUserName(clientId,userName);
            if(oauthAccessTokens!=null&&oauthAccessTokens.size()>0) {

                for (OauthAccessTokenVO accessTokenVO : oauthAccessTokens) {
                    OAuth2AccessToken oAuth2AccessToken=null;
                    try {
                        oAuth2AccessToken=this.deserializeAccessToken(accessTokenVO.getToken());
                    }catch (IllegalArgumentException var5) {
                        //根据tokenId删除token
                        String token = accessTokenVO.getTokenId();
                        oauthAccessTokenService.deleteByTokenId(token);
                    }
                    if(oAuth2AccessToken!=null){
                        accessTokens.add(oAuth2AccessToken);
                    }
                }
            }
        }catch (Exception var5) {
            log.info("Failed to find access token for clientId {}  and userName {}" ,clientId , userName);
        }
        //accessTokens = this.removeNulls((List)accessTokens);
        return accessTokens;
    }

    @Override
    public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
        Collection<OAuth2AccessToken> accessTokens = null;
        try {
            accessTokens=new ArrayList<>();
            //根据clientID查询数据库,然后序列化，得到list
            log.info("tokenStore_findTokensByClientId：clientId:{}-querydb",clientId);
            List<OauthAccessTokenVO> oauthAccessTokens=oauthAccessTokenService.selectByClientIdAndUserName(clientId,null);
            if(oauthAccessTokens!=null&&oauthAccessTokens.size()>0) {

                for (OauthAccessTokenVO accessTokenVO : oauthAccessTokens) {
                    OAuth2AccessToken oAuth2AccessToken=null;
                    try {
                        oAuth2AccessToken=this.deserializeAccessToken(accessTokenVO.getToken());
                    }catch (IllegalArgumentException var5) {
                        //根据tokenId删除token
                        String token = accessTokenVO.getTokenId();
                        oauthAccessTokenService.deleteByTokenId(token);
                    }
                    if(oAuth2AccessToken!=null){
                        accessTokens.add(oAuth2AccessToken);
                    }
                }
            }
        } catch (Exception var4) {
            log.info("Failed to find access token for clientId {}" , clientId);
        }
       // List<OAuth2AccessToken> accessTokensList = this.removeNulls((List)accessTokens);
        return accessTokens;
    }

    protected String extractTokenKey(String value) {
        if (value == null) {
            return null;
        } else {
            MessageDigest digest;
            try {
                digest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException var5) {
                throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
            }

            try {
                byte[] bytes = digest.digest(value.getBytes("UTF-8"));
                return String.format("%032x", new BigInteger(1, bytes));
            } catch (UnsupportedEncodingException var4) {
                throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
            }
        }
    }

    protected byte[] serializeAccessToken(OAuth2AccessToken token) {
        return SerializationUtils.serialize(token);
    }

    protected byte[] serializeRefreshToken(OAuth2RefreshToken token) {
        return SerializationUtils.serialize(token);
    }

    protected byte[] serializeAuthentication(OAuth2Authentication authentication) {
        return SerializationUtils.serialize(authentication);
    }

    protected OAuth2AccessToken deserializeAccessToken(byte[] token) {
        return (OAuth2AccessToken)SerializationUtils.deserialize(token);
    }

    protected OAuth2RefreshToken deserializeRefreshToken(byte[] token) {
        return (OAuth2RefreshToken)SerializationUtils.deserialize(token);
    }

    protected OAuth2Authentication deserializeAuthentication(byte[] authentication) {
        return (OAuth2Authentication)SerializationUtils.deserialize(authentication);
    }


    private List<OAuth2AccessToken> removeNulls(List<OAuth2AccessToken> accessTokens) {
        List<OAuth2AccessToken> tokens = new ArrayList();
        Iterator var3 = accessTokens.iterator();

        while(var3.hasNext()) {
            OAuth2AccessToken token = (OAuth2AccessToken)var3.next();
            if (token != null) {
                tokens.add(token);
            }
        }

        return tokens;
    }


    public boolean isExpired(Date expiration) {
        return expiration != null && expiration.before(new Date());
    }
}
